package com.zzyl.service.impl;


import cn.hutool.core.date.LocalDateTimeUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.base.ResponseResult;
import com.zzyl.dto.DeviceDataPageReqDto;
import com.zzyl.mapper.DeviceDataMapper;
import com.zzyl.service.DeviceDataService;
import com.zzyl.utils.ObjectUtil;
import com.zzyl.vo.DeviceDataGraphVo;
import com.zzyl.vo.DeviceDataVo;
import org.redisson.api.RList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Description DeviceDataServiceImpl
 * @Author chendongping
 * @Date 2025-05-22  21:04
 */
@Service
public class DeviceDataServiceImpl implements DeviceDataService {

    // 设备数据映射器，用于操作设备数据相关的数据库操作
    @Autowired
    private DeviceDataMapper deviceDataMapper;

    /**
     * 查询设备数据信息
     * 该方法根据设备数据查询请求DTO中的条件，分页查询设备数据并返回
     *
     * @param deviceDataPageReqDto 设备数据查询请求DTO，包含查询条件和分页信息
     * @return 返回一个分页响应对象，包含设备数据信息
     */
    @Override
    public PageResponse<DeviceDataVo> queryDeviceData(DeviceDataPageReqDto deviceDataPageReqDto) {
        // 启动分页助手，根据请求DTO中的页码和页面大小进行分页
        PageHelper.startPage(deviceDataPageReqDto.getPageNum(), deviceDataPageReqDto.getPageSize());

        // 前端传递的时间是一个long类型的毫秒值，作为条件查询数据库需要转换为日期类型
        Long startTime = deviceDataPageReqDto.getStartTime();
        Long endTime = deviceDataPageReqDto.getEndTime();

        // 设置转换后的开始日期和结束日期到查询请求DTO中
        deviceDataPageReqDto.setStartDate(ObjectUtil.isEmpty(startTime) ? null : LocalDateTimeUtil.of(startTime));
        deviceDataPageReqDto.setEndDate(ObjectUtil.isEmpty(endTime) ? null : LocalDateTimeUtil.of(endTime));

        // 调用设备数据映射器的查询方法，根据查询请求DTO中的条件查询设备数据
        Page<DeviceDataVo> deviceDataVos = deviceDataMapper.queryDeviceData(deviceDataPageReqDto);

        // 返回包含设备数据信息的分页响应对象
        return PageResponse.of(deviceDataVos, DeviceDataVo.class);
    }

   /**
 * 按日查询设备数据列表
 *
 * @param iotId 设备的IoT ID，用于唯一标识设备
 * @param functionId 功能ID，用于指定设备的特定功能
 * @param startTime 开始时间，用于限定查询的时间范围
 * @param endTime 结束时间，用于限定查询的时间范围
 * @return 返回按日聚合的设备数据图表对象列表
 */
@Override
public List<DeviceDataGraphVo> queryDeviceDataListByDay(String iotId, String functionId, Long startTime, Long endTime) {
    //按日聚合数据
    List<DeviceDataGraphVo> deviceDataGraphVoList = deviceDataMapper.queryDeviceDataListByDay(iotId, functionId, LocalDateTimeUtil.of(startTime), LocalDateTimeUtil.of(endTime));
    //构建一个24小时的集合
    List<DeviceDataGraphVo> list = DeviceDataGraphVo.dayInstance(LocalDateTimeUtil.of(startTime));
    //转换为map
    Map<String, Double> map = new HashMap<>();
    deviceDataGraphVoList.forEach(vo -> map.put(vo.getDateTime(), vo.getDataValue()));
    list.forEach(d -> {
        //从map获取值，如果为空，则给默认为0
        Double val = map.get(d.getDateTime()) == null ? 0.0 : map.get(d.getDateTime());
        d.setDataValue(val);
    });
    return list;
}

/**
 * 根据周时间范围查询设备数据列表
 *
 * 该方法用于从数据库中查询指定设备在指定功能和时间范围内的数据，并以周为单位进行聚合
 * 它首先调用mapper层方法获取按周聚合的数据，然后创建一个完整的周时间范围列表，并将查询到的数据映射到这个列表中
 *
 * @param iotId 设备的物联网ID，用于标识特定的设备
 * @param functionId 设备功能的ID，用于指定设备的某个特定功能
 * @param startTime 查询时间范围的开始时间，以毫秒为单位的时间戳
 * @param endTime 查询时间范围的结束时间，以毫秒为单位的时间戳
 * @return 返回一个包含设备数据的列表，每个数据点代表一周的时间范围
 */
@Override
public List<DeviceDataGraphVo> queryDeviceDataListByWeek(String iotId, String functionId, Long startTime, Long endTime) {
    //按周聚合数据
    List<DeviceDataGraphVo> deviceDataGraphVoList = deviceDataMapper.queryDeviceDataListByWeek(iotId, functionId, LocalDateTimeUtil.of(startTime), LocalDateTimeUtil.of(endTime));
    //创建一个按周时间范围实例化的设备数据列表
    List<DeviceDataGraphVo> list = DeviceDataGraphVo.weekInstance(LocalDateTimeUtil.of(startTime));
    //使用HashMap存储日期时间和对应的数据值
    Map<String, Double> map = new HashMap<>();
    //将查询到的设备数据填充到map中
    deviceDataGraphVoList.forEach(vo -> map.put(vo.getDateTime(), vo.getDataValue()));
    //遍历周时间范围列表，为每个时间点设置数据值
    list.forEach(d -> {
        //如果当前时间点没有数据，则设置为0.0，否则使用查询到的数据值
        Double val = map.get(d.getDateTime()) == null ? 0.0 : map.get(d.getDateTime());
        d.setDataValue(val);
    });
    //返回填充后的周时间范围设备数据列表
    return list;
}

}
